#ifndef __CLX_UTIL_HPP__
#define __CLX_UTIL_HPP__

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include "bundle.h"
#include "unistd.h"
#include <sys/stat.h>
#include <jsoncpp/json/json.h>
#include <experimental/filesystem>


namespace clx_cloud {
    namespace fs = std::experimental::filesystem;
    class FileUtil {
        private:
            std::string _filename;
        public:
            FileUtil(const std::string &filename) : _filename(filename){};
            int64_t FileSize() {
                struct stat st;
                if (stat(_filename.c_str(), &st) < 0) {
                    std::cout << "get file size failed! " << std::endl;
                    return -1;
                }
                return st.st_size;
            }

            time_t LastMTime() {
                struct stat st;
                if (stat(_filename.c_str(), &st) < 0) {
                    std::cout << "get file size failed! " << std::endl;
                    return -1;
                }
                return st.st_mtime;
            }

            time_t LastATime() {
                struct stat st;
                if (stat(_filename.c_str(), &st) < 0) {
                    std::cout << "get file size failed! " << std::endl;
                    return -1;
                }
                return st.st_atime;
            }

            std::string FileName() {
                size_t pos = _filename.find_last_of("/\\");
                if (pos == std::string::npos) {
                    return _filename;
                }
                return _filename.substr(pos + 1);
                // return fs::path(_filename).filename().string();
            }

            bool GetPosLen(std::string *body, size_t pos, size_t len) {
                size_t fsize = this->FileSize();
                if (pos + len > fsize) {
                    std::cout << "get file len is error" << std::endl;
                    return false;
                }
                std::ifstream ifs;
                ifs.open(_filename, std::ios::binary);
                if (ifs.is_open() == false) {
                    std::cout << "open file failed" << std::endl;
                    return false;
                }
                ifs.seekg(pos, std::ios::beg); // 从文件起始位置开始偏移到pos处
                body->resize(len);
                ifs.read(&(*body)[0], len);
                if (ifs.good() == false) {
                    std::cout << "get file Content failed" << std::endl;
                    return false;
                }
                ifs.close();
                return true;
            }

            bool GetContent(std::string *body) {
                size_t fsize = this->FileSize();
                return GetPosLen(body, 0, fsize);
            }

            bool SetContent(const std::string *body) {
                std::ofstream ofs;
                ofs.open(_filename, std::ios::binary);
                if (ofs.is_open() == false) {
                    std::cout << "write open file failed!" << std::endl;
                    return false;
                }
                ofs.write(&(*body)[0], body->size());
                if (ofs.good() == false) {
                    std::cout << "write file content failed!" << std::endl;
                    ofs.close();
                    return false;
                }
                ofs.close();
                return true;
            }

            bool Compress(const std::string &packname) {
                // 1.获取压缩文件数据
                std::string body;
                if (this->GetContent(&body) == false) {
                    std::cout << "compress get file content failed" << std::endl;
                    return false;
                }
                // 2.对数据进行结压缩
                std::string packed = bundle::pack(bundle::LZIP, body);
                // 3.将压缩到数据存储到压缩包中
                FileUtil fu(packname);
                if (fu.SetContent(&packed) == false) {
                    std::cout << "compress write packed data failed" << std::endl;
                    return false;
                }
                return true;
            }

            bool UnCompress(const std::string &filename) {
                // 1.获取原文件数据
                std::string body;
                if (this->GetContent(&body) == false) {
                    std::cout << "Uncompress get file content failed" << std::endl;
                    return false;
                }
                // 2.对数据进行压缩
                std::string unpacked = bundle::unpack(body);
                // 3.将压缩到数据存储到压缩包中
                FileUtil fu(filename);
                if (fu.SetContent(&unpacked) == false) {
                    std::cout << "uncompress write packed data failed" << std::endl;
                    return false;
                }
                return true;
            }

            bool Exists() { return fs::exists(_filename); }

            bool CreateDirectory() { 
                if (this->Exists()) return true;
                return fs::create_directories(_filename); 
            }

            bool Remove() {
                if (this->Exists() == false) return true;
                remove(_filename.c_str());
                return true;
            }

            bool ScanDirectory(std::vector<std::string> *array) {
                for (auto& entry : fs::directory_iterator(_filename)) {
                    if (!fs::is_directory(entry)) {
                        array->push_back(fs::path(entry).relative_path().string());
                    }
                }
                return true;
            }
    };

    class JsonUtil{
        public:
            static bool Serialize(const Json::Value &root, std::string *str) {
                Json::StreamWriterBuilder swb;
                std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
                std::stringstream ss;
                sw->write(root, &ss);
                *str = ss.str();
                return true;
            }
            static bool UnSerialize(const std::string &str, Json::Value *root) {
                Json::CharReaderBuilder crb;
                std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
                std::string err;
                bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), root, &err);
                if (ret == false) {
                    std::cout << "parse error: " << err << std::endl;
                    return false;
                }
                return true;
            }
    };
}

#endif
